Skip to content

Conversation

@elliotbruneel
Copy link

@elliotbruneel elliotbruneel commented Oct 30, 2025

Fix a regression introduced in #12190: $repository->findBy(['uuid' => []] should be a valid expression and return an empty result.

Fixes #12245, with additional discussion at #12190 (comment).

@greg0ire greg0ire added the Bug label Oct 30, 2025
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should probably be a functional test for this. That way we'll be sure it works with all database platforms.

@elliotbruneel elliotbruneel force-pushed the fix/empty-array-query branch 7 times, most recently from c042729 to 5d285c8 Compare October 30, 2025 14:28
@derrabus derrabus added this to the 2.20.8 milestone Oct 30, 2025
SenseException
SenseException previously approved these changes Nov 1, 2025
@greg0ire
Copy link
Member

greg0ire commented Nov 2, 2025

@mpdude please review

@webmake
Copy link

webmake commented Nov 5, 2025

Unfortunately, provided solution doesn't work in our use case, same error: Syntax error

@elliotbruneel
Copy link
Author

Unfortunately, provided solution doesn't work in our use case, same error: Syntax error

Can you give me more details, like which database system you’re using and when this happened?

@whataboutpereira
Copy link

I can confirm that it works here with MySQL, ORM v3.5.3.

$value = [$value];
}

if (empty($value)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might compare array with === [] here.

@webmake
Copy link

webmake commented Nov 5, 2025

Unfortunately, provided solution doesn't work in our use case, same error: Syntax error

Can you give me more details, like which database system you’re using and when this happened?

It looks like additional loop in expandParameters caused not resolved types in inferParameterTypes because giving one value - it returns types as array uuid_binary_ordered_time as string, while in older version it returns object typeOf UuidBinaryOrderedTimeType
image

image

it worked in doctrine/orm 2.20.6, and stopped working in doctrine/orm 2.20.7. Only one dependency updated.
we are using additionally ramsey/uuid-doctrine:1.8.2

@elliotbruneel
Copy link
Author

elliotbruneel commented Nov 5, 2025

Unfortunately, provided solution doesn't work in our use case, same error: Syntax error

Can you give me more details, like which database system you’re using and when this happened?

It looks like additional loop in expandParameters caused not resolved types in inferParameterTypes because giving one value - it returns types as array uuid_binary_ordered_time as string, while in older version it returns object typeOf UuidBinaryOrderedTimeType [...]

it worked in doctrine/orm 2.20.6, and stopped working in doctrine/orm 2.20.7. Only one dependency updated. we are using additionally ramsey/uuid-doctrine:1.8.2

I cannot reproduce your issue and it's not directetly related to my fix. Maybe create another issue ?

i have tested with https://github.com/ramsey/uuid-doctrine/tree/main?tab=readme-ov-file#innodb-optimised-binary-uuids---deprecated and i cannot reproduce it.

I have tried

<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\Tests\OrmFunctionalTestCase;

use function random_bytes;

class GH12254Test extends OrmFunctionalTestCase
{
    protected function setUp(): void
    {
        if (Type::hasType('uuid_binary_ordered_time')) {
            Type::overrideType('uuid_binary_ordered_time', UuidType::class);
        } else {
            Type::addType('uuid_binary_ordered_time', UuidType::class);
        }

        parent::setUp();

        $this->setUpEntitySchema([
            GH12254EntityB::class,
        ]);

        $this->_em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('uuid_binary_ordered_time', 'binary');
    }

    public function testGetTypeForUuidBinaryOrderedTimeType(): void
    {
        $entity     = new GH12254EntityB();
        $entity->id = random_bytes(16);

        $this->_em->persist($entity);
        $this->_em->flush();

        $result         = $this->_em->getRepository(GH12254EntityB::class)->findBy(['id' => []]);
        $resultNonEmpty = $this->_em->getRepository(GH12254EntityB::class)->findBy(['id' => [$entity->id]]);

        $this->assertEmpty($result);
        $this->assertCount(1, $resultNonEmpty);
    }
}


/**
 * @Entity()
 */
class GH12254EntityB
{
    /**
     * @Column(type="uuid_binary_ordered_time")
     * @Id()
     * @GeneratedValue(strategy="NONE")
     * @var UUidType
     */
    public $id = null;
}

class UUidType extends Type
{
    public const NAME = 'uuid_binary_ordered_time';

    public function getName(): string
    {
        return self::NAME;
    }

    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
    {
        return $platform->getBinaryTypeDeclarationSQL(
            [
                'length' => 16,
                'fixed' => true,
            ]
        );
    }
}

@webmake
Copy link

webmake commented Nov 5, 2025

It could be unrelated, I think it's working for you, because it's simple case, but Ramsey has convertToDatabaseValue and it passes binary string https://github.com/ramsey/uuid-doctrine/blob/1.8.2/src/UuidBinaryOrderedTimeType.php#L110 instead of type or uuid string format

@webmake
Copy link

webmake commented Nov 5, 2025

Sorry for confusion, I retract all my comments, it looks like we had bugfix, which doesn't work anymore. But thanks for fixing things! ❤️

@mpdude
Copy link
Contributor

mpdude commented Nov 10, 2025

@elliotbruneel Thank you for working on this!

Two remarks, probably a matter of personal preference:

  1. I'd prefer using 1=0 over IN (NULL). To me, that makes the intent more clear. I don't know if it would be better portable as well.

  2. We could amend the \Doctrine\Tests\ORM\Persisters\BasicEntityPersisterTypeValueSqlTest::testSelectConditionStatementWithMultipleValuesContainingNull test where the logic in question has been covered before.

If you're looking for a suggestion, you may want to check https://github.com/doctrine/orm/compare/2.20.x...mpdude:doctrine2:fix-12190-regression?expand=1.

@mpdude
Copy link
Contributor

mpdude commented Nov 10, 2025

Just noticed that IN (NULL) was the SQL we generated before. So, obviously, it's sufficiently portable, at least nobody complained.

@elliotbruneel
Copy link
Author

Just noticed that IN (NULL) was the SQL we generated before. So, obviously, it's sufficiently portable, at least nobody complained.

My first goal was indeed to keep original generated code. I have edited the logic as it's more clear

identifier: missingType.generics
count: 1
path: src/Internal/HydrationCompleteHandler.php

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's that coming from? Maybe you need to rebase to make it go away?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's come from the update of phpstan to 2.1.23

Copy link
Contributor

@mpdude mpdude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for the functional aspects, thank you.

Regarding PHPStan, @greg0ire please decide whether it makes sense in this PR

@greg0ire
Copy link
Member

@mpdude as long as it is in a separate commit, which is the case, I'm satisfied.

@greg0ire greg0ire merged commit 5bff091 into doctrine:2.20.x Nov 10, 2025
90 checks passed
@greg0ire
Copy link
Member

Thanks @elliotbruneel !

@zerkms
Copy link

zerkms commented Nov 10, 2025

@mpdude IN (NULL) is guaranteed to not match any rows. And IN (1=0) would match "falsy" (or real boolean false) values, depending on the DBMS.

@mpdude
Copy link
Contributor

mpdude commented Nov 10, 2025

Not IN(1=0), only 1=0

@zerkms
Copy link

zerkms commented Nov 10, 2025

@mpdude

postgresql:

# SELECT * FROM unnest(ARRAY[true, false]) AS t(col) WHERE col IN (1=0);
 col 
-----
 f
(1 row)

# SELECT * FROM unnest(ARRAY[null, true, false]) AS t(col) WHERE col IN (null);
 col 
-----
(0 rows)

@mpdude oh I see now, the suggestion was not to have IN (...) operator at all, just 1=0 expression. Right, my fault.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants